package com.si.control.action;

import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import javax.imageio.ImageIO;
import javax.swing.JProgressBar;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.si.ui.enumn.EConstant;

public class SearchAction implements IAction<String, List<BufferedImage>>, Runnable {

	private final String address;
	private final String tail;
	private final ExecutorService executor;
	
	// Can only reach 90%.
	private final JProgressBar bar;

	private volatile boolean finished;
	private volatile int currentLimit;
	
	public SearchAction(final JProgressBar bar) {
		this.address = "http://ajax.googleapis.com/ajax/services/search/images?start=";
		this.tail = "&rsz=large&v=1.0&q=";
		this.executor = Executors.newSingleThreadExecutor();
		this.bar = bar;
	}

	@Override
	public void run() {
		int i = 0;
		while(!this.finished) {
			while(i < this.currentLimit) {
				this.bar.setValue(i);
				try {TimeUnit.MILLISECONDS.sleep(180);} catch (InterruptedException e) {e.printStackTrace();}
				i++;
			}
			try {TimeUnit.MILLISECONDS.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}
		}
	}

	@Override
	public List<BufferedImage> perform(final String argument) throws Exception {
		if(argument == null || argument.length() <= 0) throw new IllegalArgumentException("Search sentence must be at least one word.");
		final List<BufferedImage> images = new ArrayList<BufferedImage>();
		final List<String> list = new ArrayList<String>();
		final StringBuilder builder = new StringBuilder();
		final char[] chars = argument.toCharArray();
		boolean quote = false;
		for(int i = 0; i < chars.length; i++) {
			final char c = chars[i];
			if(c == ' ' && !quote) {
				if(builder.length() > 0) list.add(builder.toString());
				builder.delete(0, builder.length());
			} else if(c == '\"') {
				if(quote) {
					if(builder.length() > 0) list.add(builder.toString());
					builder.delete(0, builder.length());
				}
				quote = !quote;
			} else {
				builder.append(c);
				if(i == chars.length-1 && builder.length() > 0) list.add(builder.toString());
			}
		}
		final String[] words = new String[list.size()];
		for(int i = 0; i < words.length; i++) words[i] = list.get(i);
		final int progressPerWord = 90/words.length;
		this.finished = false;
		this.executor.submit(this);
		for(int i = 0; i < words.length; i++) {
			final String word = words[i];
			this.bar.setString("Searching images for word \"".concat(word).concat("\" ..."));
			this.currentLimit = progressPerWord * (i+1);
			final URL[] urls = this.searchResult(word);
			URL url = null;
			try {
				for(int j = 0; j < urls.length; j++) {
					url = urls[j];
					if(url == null) continue;
					final BufferedImage image = ImageIO.read(url);
					// Some times IO messes up.
					if(image != null) images.add(image);
				}
			} catch(IOException e) {
				System.err.println("Cannot retrieve from: ".concat(url.toString()));
			}
			this.bar.setValue(this.currentLimit);
		}
		this.finished = true;
		this.currentLimit = 0;
		return images;
	}

	private URL[] searchResult(final String word) throws IOException, JSONException{
		final int perword = EConstant.ResultPerWord.getValue();
		final URL[] results = new URL[perword];
		int start = 0;
		int count = 0;
		while(count < perword) {
			JSONArray jarray = this.retrieveArray(word, start);
			final int jlength = jarray.length();
			if(jlength == 0) break;
			final int leftover = perword - count;
			final int length = Math.min(leftover, jlength);
			for(int i = 0; i < length; i++) {
				final JSONObject urlobject = jarray.getJSONObject(i);
				results[start+i] = new URL(urlobject.getString("url"));
				count++;
			}
			start = jlength;
		}
		return results;
	}
	
	private JSONArray retrieveArray(final String word, final int start) throws IOException, JSONException {
		final String query = this.address.concat(String.valueOf(start)).concat(this.tail);
		final URL url = new URL(query.concat(URLEncoder.encode(word, "UTF-8")));
		final URLConnection connection = url.openConnection();
		final StringBuilder builder = new StringBuilder();
		final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
		String line;
		while((line = reader.readLine()) != null) {
			builder.append(line);
		}
		final JSONObject response = new JSONObject(builder.toString());
		return response.getJSONObject("responseData").getJSONArray("results");
	}
}
